package org.liujing.jsw.client.ui;

import com.google.gwt.user.client.ui.*;
import org.liujing.jsw.client.ui.plaf.*;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.*;
import org.liujing.jsw.client.border.*;
//import javax.swing.InputVerifier;
import java.util.*;
import static org.liujing.jsw.client.ui.JComponent.*;
import java.util.logging.*;

@SuppressWarnings("deprecation")
public class ComponentImpl{
	private static Logger log = Logger.getLogger("client.ComponentImpl");
	int x;
    int y;
    int width;
    int height;
    boolean isPacked = false;
	private boolean isAlignmentXSet;
	private float alignmentX;
	private boolean isAlignmentYSet;
	private float alignmentY;
	private boolean autoscrolls;
	private Border border;
	private int flags;
	
	/* Input verifier for this component */
	private InputVerifier inputVerifier = null;
	boolean enabled = true;
    boolean valid = false;
	//DropTarget dropTarget;
    Vector popups;
	private boolean verifyInputWhenFocusTarget = true;
	private boolean focusable = true;
	Dimension minSize;
	transient ComponentOrientation componentOrientation = ComponentOrientation.UNKNOWN;
    boolean minSizeSet;
    Dimension prefSize;
    boolean prefSizeSet;
    Dimension maxSize;
    boolean maxSizeSet;
    
    protected Container parent;
    protected transient ComponentUI ui;
    
	protected boolean elementSet = false;
	
	protected JComponent gwtWidget;
	
	protected Element contentEl;
	
	protected int childOffsetX = 0;
	protected int childOffsetY = 0;
	
	public ComponentImpl(JComponent w){
		gwtWidget = w;
		//updateUI();
	}
	
	public void setChildElementOffset(int x, int y){
		childOffsetX = x;
		childOffsetY = y;
	}
	
	public int getChildElementOffsetX(){
		return childOffsetX;
	}
	
	public int getChildElementOffsetY(){
		return childOffsetY;
	}
	
	public void updateUI() {
		setUI(UIManager.getUI(gwtWidget));
	}
	
	public void setUI(ComponentUI newUI) {
        /* We do not check that the UI instance is different
         * before allowing the switch in order to enable the
         * same UI instance *with different default settings*
         * to be installed.
         */
        if (ui != null) {
            ui.uninstallUI(gwtWidget);
        }
        
        ComponentUI oldUI = ui;
        ui = newUI;
        if (ui != null) {
            ui.installUI(gwtWidget);
        }

        //firePropertyChange("UI", oldUI, newUI);
        //revalidate();
        //repaint();
    }
    
	public void onResize(){
	}
	
	public void onAttach(){
	}
	
	public void onload(){
		ui.onComponentLoad(gwtWidget);
	}
	
	public void onDetach(){
		
	}
	public void addNotify() {
		invalidate();

        //int npopups = (popups != null? popups.size() : 0);
        //for (int i = 0 ; i < npopups ; i++) {
        //    PopupMenu popup = (PopupMenu)popups.elementAt(i);
        //    popup.addNotify();
        //}
		//firePropertyChange("ancestor", null, getParent());

		//registerWithKeyboardManager(false);
		//registerNextFocusableComponent();
	}
	
	public void setDomElement(com.google.gwt.dom.client.Element elem) {
		elementSet = true;
		if(contentEl == null)
			contentEl = elem;
	}
	
	public void setContentElement(com.google.gwt.dom.client.Element elem) {
		contentEl = elem;
	}
	
	public Element getContentElement(){
		return contentEl;
	}
	
	public boolean isDomElementSet(){
		return elementSet;
	}
	
	public void removeNotify() {
		int npopups = (popups != null? popups.size() : 0);
        //for (int i = 0 ; i < npopups ; i++) {
        //    PopupMenu popup = (PopupMenu)popups.elementAt(i);
        //    popup.removeNotify();
        //}
		//firePropertyChange("ancestor", getParent(), null);

		//unregisterWithKeyboardManager();
		//deregisterNextFocusableComponent();

		if (autoscrolls) {
			//Autoscroller.stop(this);
		}
	}
	
	public void setBorder(Border border) {
        Border oldBorder = this.border;

        this.border = border;
        //firePropertyChange("border", oldBorder, border);
        if (border != oldBorder) {
        	if(oldBorder != null)
        		oldBorder.removeBorder(gwtWidget);
            if (border == null || oldBorder == null ||
                !(border.getBorderInsets(gwtWidget).equals(oldBorder.getBorderInsets(gwtWidget)))) {
                revalidate();
            } 
            getBorder().createBorder(gwtWidget, 0, 0, width, height);
        }
    }
    
    public Border getBorder() {
        return border;
    }
	
	public Container getParent(){
		return parent;
	}
	
	public void setParent(Container c){
		parent = c;
	}
	
	public void invalidate() {
		//JComponent parent = (JComponent)getParent();
        valid = false;
        if (!isPreferredSizeSet()) {
            prefSize = null;
        }
        if (!isMinimumSizeSet()) {
            minSize = null;
        }
        if (!isMaximumSizeSet()) {
            maxSize = null;
        }
        if (parent != null && parent.isValid()) {
            parent.invalidate();
        }
    }
    
    public void setPreferredSize(Dimension preferredSize) {
        Dimension old;
        if (prefSizeSet) {
            old = this.prefSize;
        }
        else {
            old = null;
        }
        this.prefSize = preferredSize;
        prefSizeSet = (preferredSize != null);
        //firePropertyChange("preferredSize", old, preferredSize);
    }


    /**
     * Returns true if the preferred size has been set to a
     * non-<code>null</code> value otherwise returns false.
     *
     * @return true if <code>setPreferredSize</code> has been invoked
     *         with a non-null value.
     * @since 1.5
     */ 
    public boolean isPreferredSizeSet() { 
        return prefSizeSet;
    }


    /**
     * Gets the preferred size of this component.
     * @return a dimension object indicating this component's preferred size
     * @see #getMinimumSize
     * @see LayoutManager
     */
    public Dimension getPreferredSize() {
    	if (isPreferredSizeSet()) {
    		log.fine("getPreferredSize() return preferred size "+ preferredSize());
            return preferredSize();
        }
        log.fine("getPreferredSize() return ui preferred size " + getClass().getName());
        Dimension size = null;
        if (ui != null) {
            size = ui.getPreferredSize(gwtWidget);
        }
        return (size != null) ? size : preferredSize();
    }


    /**
     * @deprecated As of JDK version 1.1,
     * replaced by <code>getPreferredSize()</code>.
     */
    @Deprecated
    public Dimension preferredSize() {
        Dimension dim = prefSize;
        if (dim == null || !(isPreferredSizeSet() || isValid())) {
            //synchronized (getTreeLock()) {
                prefSize = getMinimumSize();
                dim = prefSize;
           // }
        }
        return new Dimension(dim);
    }

    /**
     * Sets the minimum size of this component to a constant
     * value.  Subsequent calls to <code>getMinimumSize</code> will always
     * return this value.  Setting the minimum size to <code>null</code>
     * restores the default behavior.
     *
     * @param minimumSize the new minimum size of this component
     * @see #getMinimumSize
     * @see #isMinimumSizeSet
     * @since 1.5
     */
    public void setMinimumSize(Dimension minimumSize) {
        Dimension old;
        if (minSizeSet) {
            old = this.minSize;
        }
        else {
            old = null;
        }
        this.minSize = minimumSize;
        minSizeSet = (minimumSize != null);
        //firePropertyChange("minimumSize", old, minimumSize);
    }

    /**
     * Returns whether or not <code>setMinimumSize</code> has been
     * invoked with a non-null value.
     *
     * @return true if <code>setMinimumSize</code> has been invoked with a
     *              non-null value.
     * @since 1.5
     */
    public boolean isMinimumSizeSet() {
        return minSizeSet;
    }

    /**
     * Gets the mininimum size of this component.
     * @return a dimension object indicating this component's minimum size
     * @see #getPreferredSize
     * @see LayoutManager
     */
    public Dimension getMinimumSize() {
    	if (isMinimumSizeSet()) {
            return minimumSize();
        }
        Dimension size = null;
        if (ui != null) {
            size = ui.getMinimumSize(gwtWidget);
        }
        return (size != null) ? size : minimumSize();
    }

    /**
     * @deprecated As of JDK version 1.1,
     * replaced by <code>getMinimumSize()</code>.
     */
    @Deprecated
    public Dimension minimumSize() {
        /* Avoid grabbing the lock if a reasonable cached size value
         * is available.
         */
        Dimension dim = minSize;
        if (dim == null || !(isMinimumSizeSet() || isValid())) {
            //synchronized (getTreeLock()) {
                minSize = size();
                dim = minSize;
            //}
        }
        return new Dimension(dim);
    }

    /**
     * Sets the maximum size of this component to a constant
     * value.  Subsequent calls to <code>getMaximumSize</code> will always
     * return this value.  Setting the maximum size to <code>null</code>
     * restores the default behavior.
     *
     * @param maximumSize a <code>Dimension</code> containing the 
     *          desired maximum allowable size
     * @see #getMaximumSize
     * @see #isMaximumSizeSet
     * @since 1.5
     */
    public void setMaximumSize(Dimension maximumSize) {
        // If the maximum size was set, use it as the old value, otherwise
        // use null to indicate we didn't previously have a set maximum
        // size.
        Dimension old;
        if (maxSizeSet) {
            old = this.maxSize;
        }
        else {
            old = null;
        }
        this.maxSize = maximumSize;
        maxSizeSet = (maximumSize != null);
        //firePropertyChange("maximumSize", old, maximumSize);
    }

    /**
     * Returns true if the maximum size has been set to a non-<code>null</code>
     * value otherwise returns false.
     *
     * @return true if <code>maximumSize</code> is non-<code>null</code>,
     *          false otherwise
     * @since 1.5
     */ 
    public boolean isMaximumSizeSet() { 
        return maxSizeSet;
    }

    /**
     * Gets the maximum size of this component.
     * @return a dimension object indicating this component's maximum size
     * @see #getMinimumSize
     * @see #getPreferredSize
     * @see LayoutManager
     */
    public Dimension getMaximumSize() {
        if (isMaximumSizeSet()) {
            return new Dimension(maxSize);
        }
        Dimension size = null;
        if (ui != null) {
            size = ui.getMaximumSize(gwtWidget);
        }
        return (size != null) ? size : new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
    }
    
    public Dimension getSize() {
        return size();
    }

    /**
     * @deprecated As of JDK version 1.1,
     * replaced by <code>getSize()</code>.
     */
    @Deprecated
    public Dimension size() {
        return new Dimension(width, height);
    }
    
    public void setSize(int width, int height) {
        resize(width, height);
    }

    @Deprecated
    public void resize(int width, int height) {
        //synchronized(getTreeLock()) {
            //setBoundsOp(ComponentPeer.SET_SIZE);            
            setBounds(x, y, width, height);
        //}
    }
    
    public void setBounds(int x, int y, int width, int height) {
        reshape(x, y, width, height);
    }

    @Deprecated
    public void reshape(int x, int y, int width, int height) {
      
            try {
                //setBoundsOp(ComponentPeer.SET_BOUNDS);
                boolean resized = (this.width != width) || (this.height != height);
                boolean moved = (this.x != x) || (this.y != y);
                //log.info("reshape() " + gwtWidget.getClass().getName() +"-"+ hashCode() + ": x=" + x + " y=" + y + " w=" + width + " h=" + height);
                
                if (!resized && !moved) {
                    return;
                }
                int oldX = this.x;
                int oldY = this.y;
                int oldWidth = this.width; 
                int oldHeight = this.height;
                this.x = x; 
                this.y = y;
                this.width = width; 
                this.height = height;
                
                if (resized) {
                	//if(isDomElementSet() && getParent() != null){
                	if(isDomElementSet()){
				Style style = gwtWidget.getDomElement().getStyle();
				Dimension mutableSize = new Dimension(width, height);				
				if(ui != null)
					ui.resize(gwtWidget, mutableSize);
				if(border != null){
					log.info("set border size " + mutableSize);
					border.resize(gwtWidget, mutableSize);
				}
				style.setWidth(mutableSize.width, Style.Unit.PX);
				style.setHeight(mutableSize.height, Style.Unit.PX);						
                	}
                    isPacked = false;
                }
                
                if(moved){
                	if(isDomElementSet() && getParent() != null ){
						Style style = gwtWidget.getDomElement().getStyle();
						//GWT.log("reshape() " + gwtWidget.getClass().getName() +"-"+ hashCode() +": x=" + x + " y=" + y);
						Container parent = getParent();
						
						style.setLeft(x + parent.getChildElementOffsetX(), Style.Unit.PX);
						style.setTop(y + parent.getChildElementOffsetY(), Style.Unit.PX);
                	}
                }
                
                boolean needNotify = true;
                //if (peer != null) {
                    // LightwightPeer is an empty stub so can skip peer.reshape
                    
                    if (resized) {
                        invalidate();
                    }
                    if (parent != null && parent.isValid()) {
                        parent.invalidate();
                    }
                //}
                if (needNotify) {
                    notifyNewBounds(resized, moved);
                }
                //repaintParentIfNeeded(oldX, oldY, oldWidth, oldHeight);
            } finally {
                //setBoundsOp(ComponentPeer.RESET_OPERATION);
            }
        
    }
    
    private static native void setElSize(Element el, int w, int h)/*-{
    	el.style.width = w + "px";
    	el.style.height = h + "px";
    }-*/;
    
    private void notifyNewBounds(boolean resized, boolean moved) {
    	//todo
       /* if (componentListener != null   
            || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0   
            || Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK))   
            {
                if (resized) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_RESIZED);
                    Toolkit.getEventQueue().postEvent(e);
                }
                if (moved) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_MOVED);
                    Toolkit.getEventQueue().postEvent(e);
                }
            } else {
                if (this instanceof Container && ((Container)this).countComponents() > 0) { 
                    boolean enabledOnToolkit =  
                        Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK); 
                    if (resized) {              
                        
                        ((Container)this).createChildHierarchyEvents( 
                                                                     HierarchyEvent.ANCESTOR_RESIZED, 0, enabledOnToolkit); 
                    } 
                    if (moved) { 
                        ((Container)this).createChildHierarchyEvents( 
                                                                     HierarchyEvent.ANCESTOR_MOVED, 0, enabledOnToolkit); 
                    }
                }
                }*/
    }

    public void setSize(Dimension d) {
        resize(d);
    }

    /**
     * @deprecated As of JDK version 1.1,
     * replaced by <code>setSize(Dimension)</code>.
     */
    @Deprecated
    public void resize(Dimension d) {
        setSize(d.width, d.height);
    }
    
    public float getAlignmentX() {
    	if (isAlignmentXSet) {
            return alignmentX;
        }
        return CENTER_ALIGNMENT;
    }

    public float getAlignmentY() {
    	if (isAlignmentYSet) {
            return alignmentY;
        }
        return CENTER_ALIGNMENT;
    }
    
    public void setAlignmentY(float alignmentY) {
        this.alignmentY = alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f : alignmentY;
        isAlignmentYSet = true;
    }
    
    public void setAlignmentX(float alignmentX) {
        this.alignmentX = alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX;
        isAlignmentXSet = true;
    }
    
    public boolean isValid() {
        return valid;
    }
    
    public ComponentOrientation getComponentOrientation() {
        return componentOrientation;
    }
    

    
    public Insets getInsets() {
        if (border != null) {
            return border.getBorderInsets(gwtWidget);
        }
        return new Insets(0, 0, 0, 0);
    }
    
    public boolean contains(int x, int y) {
        return inside(x, y);
    }

    /**
     * @deprecated As of JDK version 1.1,
     * replaced by contains(int, int).
     */
    @Deprecated
    public boolean inside(int x, int y) {
        return (x >= 0) && (x < width) && (y >= 0) && (y < height);
    }
    
    public void revalidate() {
        if (getParent() == null) {
            return;
        }
        //if (SwingUtilities.isEventDispatchThread()) {
        invalidate();
        //RepaintManager.currentManager(this).addInvalidComponent(this);
        RepaintManager.addInvalidComponent(gwtWidget);
        //}
    }
    
    public boolean isVisible() {
    	if(isDomElementSet())
    		return gwtWidget.isVisible();
    	else
    		return true;
    }
}
